package cn.jingyuan.swan.system.service.impl;

import cn.jingyuan.bee.utils.ObjectUtils;
import cn.jingyuan.bee.utils.RandomUtils;
import cn.jingyuan.bee.utils.bean.BeanUtils;
import cn.jingyuan.swan.cloud.core.WarnExceptionUtils;
import cn.jingyuan.swan.cloud.oauth2.DefaultAuthority;
import cn.jingyuan.swan.system.domain.enums.PersistEnum;
import cn.jingyuan.swan.system.domain.sys.dao.AppDao;
import cn.jingyuan.swan.system.domain.sys.entity.App;
import cn.jingyuan.swan.system.helper.SysAuthorityHelper;
import cn.jingyuan.swan.system.model.ClientDetails;
import cn.jingyuan.swan.system.model.SimpleAuthority;
import cn.jingyuan.swan.system.service.AppService;
import cn.jingyuan.swan.system.service.AuthorityService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class AppServiceImpl implements AppService {

    /** token 有效期, 默认: 7 天 */
    public static final int ACCESS_TOKEN_VALIDITY_SECONDS = 60 * 60 * 24 * 7;

    /** 刷新 token 有效期, 默认: 30 天 */
    public static final int REFRESH_TOKEN_VALIDITY_SECONDS = 60 * 60 * 24 * 30;

    @Resource
    AppDao appDao;

    @Resource
    AuthorityService authorityService;

    @Resource
    JdbcClientDetailsService jdbcClientDetailsService;


    @Override
    public App addAppInfo(App app) {
        String appId = RandomUtils.randomNumbers(19);
        String apiKey = RandomUtils.randomNumbers(24);
        String secretKey = RandomUtils.randomString(32);

        app.setAppId(appId);
        app.setApiKey(apiKey);
        app.setSecretKey(secretKey);
        app.setCreateTime(new Date());
        app.setUpdateTime(app.getCreateTime());

        if (ObjectUtils.isNull(app.getIsPersist())) {
            app.setIsPersist(PersistEnum.NO);
        }

        Map additionalMap = BeanUtils.beanToMap(app);

        // 功能授权
        BaseClientDetails client = new BaseClientDetails();
        client.setClientId(app.getApiKey());
        client.setClientSecret(app.getSecretKey());
        client.setAdditionalInformation(additionalMap);
        client.setAuthorizedGrantTypes(Arrays.asList("authorization_code", "client_credentials", "implicit", "refresh_token"));
        client.setAccessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS);
        client.setRefreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS);

        appDao.insert(app);
        jdbcClientDetailsService.addClientDetails(client);

        return appDao.get(appId);
    }

    @Override
    public void removeApp(String appId) {
        App appInfo = this.getAppInfo(appId);
        if (null == appInfo) {
            throw WarnExceptionUtils.newInstance("应用不存在");
        }

        if (PersistEnum.YES == appInfo.getIsPersist()) {
            throw WarnExceptionUtils.newInstance("保留数据，不允许删除！");
        }

        // 移除应用权限
        jdbcClientDetailsService.removeClientDetails(appInfo.getApiKey());
        authorityService.removeAuthorityApp(appId);
        appDao.delete(appId);
    }

    @Override
    public App updateInfo(App app) {
        app.setUpdateTime(new Date());
        appDao.update(app);

        // 修改客户端附加信息
        App appInfo = this.getAppInfo(app.getAppId());

        Map<String, Object> appInfoMap = BeanUtils.beanToMap(appInfo);
        BaseClientDetails client = (BaseClientDetails) jdbcClientDetailsService.loadClientByClientId(appInfo.getApiKey());
        client.setAdditionalInformation(appInfoMap);
        jdbcClientDetailsService.updateClientDetails(client);

        return appInfo;
    }

    @Override
    public String restSecret(String appId) {
        App appInfo = this.getAppInfo(appId);
        if (null == appInfo) {
            throw WarnExceptionUtils.newInstance("应用不存在");
        }

        if (appInfo.getIsPersist().equals(1)) {
            throw WarnExceptionUtils.newInstance("保留数据，不允许修改！");
        }

        // 新的密钥
        String secretKey = RandomUtils.randomString(32);

        appInfo.setSecretKey(secretKey);
        appInfo.setUpdateTime(new Date());

        appDao.update(appInfo);
        jdbcClientDetailsService.updateClientSecret(appInfo.getApiKey(), secretKey);

        return secretKey;
    }

    @Override
    public void updateAppClientInfo(BaseClientDetails client) {
        jdbcClientDetailsService.updateClientDetails(client);
    }

    @Override
    public App getAppInfo(String appId) {
        return appDao.get(appId);
    }

    @Override
    public List<App> getAppsInfo() {
        return appDao.listByQueryWrapper(null);
    }

    @Override
    public BaseClientDetails getBaseClientDetails(String clientId) {
        BaseClientDetails baseClientDetails = this.loadClientByClientId(clientId);

        String appId = baseClientDetails.getAdditionalInformation().get("appId").toString();

        List<SimpleAuthority> authorities = authorityService.findAuthorityByApp(appId);
        Collection<DefaultAuthority> defaultAuthorities = SysAuthorityHelper.to(authorities);

        BaseClientDetails clientDetails = new BaseClientDetails();
        BeanUtils.copyProperties(baseClientDetails, clientDetails);
        clientDetails.setAuthorities(defaultAuthorities);

        return clientDetails;
    }

    @Override
    public ClientDetails getSysClientDetails(String clientId) {
        BaseClientDetails baseClientDetails = this.loadClientByClientId(clientId);

        String appId = baseClientDetails.getAdditionalInformation().get("appId").toString();

        List<SimpleAuthority> authorities = authorityService.findAuthorityByApp(appId);

        ClientDetails clientDetails = new ClientDetails();
        BeanUtils.copyProperties(baseClientDetails, clientDetails);
        clientDetails.setAuthorities(authorities);

        return clientDetails;
    }

    private BaseClientDetails loadClientByClientId(String clientId) {
        BaseClientDetails baseClientDetails;
        try {
            baseClientDetails = (BaseClientDetails) jdbcClientDetailsService.loadClientByClientId(clientId);
        } catch (Exception ex) {
            log.error("查询 ClientDetails 出错", ex);
            return null;
        }
        return baseClientDetails;
    }

}
